from pyjamas import Window
from pyjamas.JSONService import JSONProxy
from pyjamas.ui import HasAlignment
from pyjamas.ui.Button import Button
from pyjamas.ui.CaptionPanel import CaptionPanel
from pyjamas.ui.DockPanel import DockPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.Label import Label
from pyjamas.ui.ListBox import ListBox
from pyjamas.ui.HTML import HTML
from pyjamas.ui.HTMLPanel import HTMLPanel
from pyjamas.ui.Hyperlink import Hyperlink
from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.ScrollPanel import ScrollPanel
from pyjamas.ui.SimplePanel import SimplePanel
from pyjamas.ui.TextBox import TextBox
from pyjamas.ui.Tree import Tree
from pyjamas.ui.TreeItem import TreeItem
from pyjamas.ui.VerticalPanel import VerticalPanel

NEW_REFERENCE_PROVIDER_NAME = "NewReferenceProvider"
NEW_REFERENCE_PROVIDER_DESCRIPTION = "Creates a new model"

NEW_BACK_REFERENCE_PROVIDER_NAME = "NewBackReferenceProvider"
NEW_BACK_REFERENCE_PROVIDER_DESCRIPTION = "Creates a new back-referenced model"

PARENT_MODEL_LINK = """This model is defined as a reference to another model.<br>
Click here to go to the parent model."""

DEFAULT_CONTENTS = """
<h1>Welcome to Data Seeder</h1>
"""

DEFAULT_SIDEBAR_CONTENTS = """
<p>
To get started, choose a model from the listbox above and press the
<b>Add model</b> button
</p>
"""

HELP_CONTENTS = """
<h2>Help</h2>

<h3>Model</h3>
A model is an entity kind that you can populate the datastore with. The list of
available models can be found at the top left side. Just press <b>Add model</b>
to add a model to the <b>Model hierarchy</b>.
<br><br>
After adding more models and references to other models, the <b>Model
Hierarchy</b> panel will provide an overview of the seeding operation.

<h3>Configuring a model with data providers</h3>
After selecting a model in the <b>Model Hierarchy</b>, the center panel will
contain a description of the model and some controls to configure the
number of models to seed and the data providers to use for each model property.
<br><br>
A <b>data provider</b> is an object that feeds data into the properties of a
model. Each property type can accept a number of data providers. Select a data
provider for more details on how it works.
<br><br>
If you leave <b>None</b> as a data provider, then no value will be provided for
that property, or it will take a default value, if it exists.

<h3>Adding references</h3>
When choosing <b>NewReferenceProvider</b> or <b>NewBackReferenceProvider</b> as
a data provider for a reference property, a new model will be added to the
model hierarchy, which will be linked back to the orignal model using the data
provider.
<br><br>
When using <b>NewReferenceProvider</b>, a <i>single</i> model will be added and
linked back to the original model.
<br><br>
When using <b>NewBackReferenceProvider</b>, you can configure a number of
models that will be linked back to the original model, because the
<i>ReferenceProperty</i> is actually in the second model, not the original one.
<br><br>
The original model simply contains a <b>_ReverseReferenceProperty</b>, which in
this case is nothing more than a hint that other models can link to this model.
"""


class DataSeederProxy(JSONProxy):
  """Proxy object for accessing JSON-RPC methods on the server."""
  def __init__(self):
    JSONProxy.__init__(self, "/seeder/seeder.json", ["getData", "seed"])


class ModelData(object):
  """Holds meta information about a model."""

  models = {}

  def __init__(self, data):
    """Builds the ModelData object.

    Args:
      data: A dict containing the model meta information

    """
    self.name = data.get("name")
    self.description = data.get("description")
    self.parent = data.get("parent")

    self.properties = {}
    properties = data.get("properties")
    if properties:
      for property_name, property_data in properties.iteritems():
        self.properties[property_name] = PropertyData(property_data)

  @staticmethod
  def addModel(data):
    """Adds a new ModelData object to the collection of models.

    Args:
      data: A dict containing the model meta information
    """
    model_data = ModelData(data)
    ModelData.models[model_data.name] = model_data

  @staticmethod
  def getModelNames():
    """Returns a list of the names of all models (ModelData) added to the
    collection.

    """
    return ModelData.models.keys()

  @staticmethod
  def getModel(model_name):
    """Returns a ModelData object from the collection or None if not found.

    Args:
      model_name: The name of the model to return.

    """
    return ModelData.models.get(model_name)


class PropertyData(object):
  """Holds meta information about a model property."""

  def __init__(self, data):
    """Builds the PropertyData object.

    Args:
      data: A dict containing the property meta information

    """
    self.name = data.get("name")
    self.verbose_name = data.get("verbose_name")
    self.type = data.get("type")
    self.required = data.get("required")
    self.choices = data.get("choices")
    self.group = data.get("group")
    self.reference_class = data.get("reference_class")
    self.reverse_property = data.get("reverse_property")


class DataProviderData(object):
  """Holds meta information about a general data provider."""

  data_providers = {}
  providers_mapping = {}

  def __init__(self, data):
    """Builds the DataProviderData object.

    Args:
      data: A dict containing the data provider meta information

    """
    self.name = data.get("name")
    self.description = data.get("description")

  @staticmethod
  def getDataProvider(provider_name):
    """Adds a new DataProviderData to the collection of data providers."""
    return DataProviderData.data_providers.get(provider_name)

  @staticmethod
  def getDataProvidersForProperty(property_type):
    """Returns a list of data providers compatible with a property.

    Args:
      property_type: The name of the property type

    """
    return DataProviderData.providers_mapping.get(property_type, [])

  @staticmethod
  def addDataProvider(data_provider_data):
    """Adds a new DataProviderData object to the collection of data providers.

    Args:
      data_provider_data: A DataProviderData object to add to the collection.

    """
    DataProviderData.data_providers[data_provider_data.name] = data_provider_data
    return data_provider_data

  @staticmethod
  def addProviderMapping(property_type, provider_name):
    """Adds a mapping between a property type and a compatible data provider.

    Args:
      property_type: The name of the property type
      provider_name: The name of the compatible data provider

    """
    provider = DataProviderData.getDataProvider(provider_name)
    if provider:
      providers = DataProviderData.providers_mapping.get(property_type, [])
      providers.append(provider)
      DataProviderData.providers_mapping[property_type] = providers

class NormalDataProviderData(DataProviderData):
  """Holds meta information about a normal data provider (not a reference)."""

  def __init__(self, data):
    """Builds the NormalDataProviderData object.

    Args:
      data: A dict containing the data provider meta information

    """
    super(NormalDataProviderData, self).__init__(data)
    self.parameters = {}
    parameters = data.get("parameters")
    if parameters:
      for parameter_name, parameter_data in parameters.iteritems():
        self.parameters[parameter_name] = ParameterData(parameter_data)


class ReferenceDataProviderData(DataProviderData):
  """Holds meta information about a normal data provider seeding a reference."""

  def __init__(self, data):
    """Builds the DataProviderData object.

    Args:
      data: A dict containing the data provider meta information

    """
    super(ReferenceDataProviderData, self).__init__(data)


class ParameterData(object):
  """Holds meta information about a data provider parameter."""

  def __init__(self, data):
    """Builds the ParameterData object.

    Args:
      data: A dict containing the data provider parameter meta information

    """
    self.name = data.get("name")
    self.verbose_name = data.get("verbose_name")
    self.description = data.get("description")
    self.required = data.get("required")

class Model(object):
  """Holds information about a specific model that will be seeded."""

  def __init__(self, model_data, parent_reference_parameter=None,
               single_model=False, ignore_property=None):
    """Buil a Model object using meta information from a ModelData object.

    Args:
      model_data: A ModelData object to use for meta information.
      parent_reference_parameter: The parent ReferenceParameter, in case this
                                  model is a reference.
      single_model: Set to True if this represents a single model, as
                    opposed to multiple models to seed.
      ignore_property: The name of a property to ignore. Used to for hiding
                       the reverse of a reference property for referenced
                       models.

    """
    self.model_data = model_data
    self.single_model = single_model
    self.ignore_property=None
    self.parent_reference_parameter = parent_reference_parameter

    properties = model_data.properties

    self.properties = []
    for _, property in properties.iteritems():
      if property.name != ignore_property:
        self.properties.append(Property(property, self))

    self.widget = ModelWidget(self)
    self.tree_item = TreeItem(model_data.name)
    self.tree_item.setUserObject(self)

    if self.parent_reference_parameter:
      parent_model = self.getParent()
      parent_model.tree_item.addItem(self.tree_item)
      parent_model.tree_item.setState(True, True)
    else:
      seeder.tree.addItem(self.tree_item)
    #seeder.tree.setSelectedItem(self.tree_item, True)

  def getParent(self):
    """Return the parent model, in case this model was created as a reference
    to another model.
    """
    if self.parent_reference_parameter:
      return self.parent_reference_parameter.data_provider.property.model
    return None

  def remove(self, check_parent=True):
    """Removes this model from the configuration.

    Args:
      check_parent: If set to True, will check for a parent ReferenceProvider,
                    and remove the data provider.
    """
    if check_parent and self.parent_reference_parameter:
      property = self.parent_reference_parameter.data_provider.property
      property.getWidget().data_provider_listbox.setSelectedIndex(0)
      property.clearDataProvider()
    self.tree_item.remove()
    if seeder.tree.getItemCount() == 0:
      seeder.tree_panel.clear()
      seeder.tree_panel.add(HTML(DEFAULT_SIDEBAR_CONTENTS))


  def getWidget(self):
    """Returns the widget used for seeding this model."""
    return self.widget

  def getNumber(self):
    """Returns the number of models to seed."""
    try:
      number = self.widget.number_text_box.getText()
    except ValueError:
      Window.alert("Not a number!")
      return 0
    return number

  def getConfigurationSheet(self):
    """Returns the configuration sheet for seeding this model."""
    configuration_sheet = {}
    configuration_sheet['name'] = self.model_data.name
    if not self.single_model:
      configuration_sheet['number'] = self.getNumber()
    properties = {}

    for property in self.properties:
      property_configuration_sheet = property.getConfigurationSheet()
      if property_configuration_sheet:
        properties[property.getName()] = property_configuration_sheet

    if properties:
      configuration_sheet['properties'] = properties

    return configuration_sheet


class Property(object):
  """Holds information about a specific property of a model that will be seeded."""

  def __init__(self, property_data, model):
    """Buil a Property object using meta information from a PropertyData object.

    Args:
      property_data: A PropertyData object to use for meta information.

    """
    self.model = model
    self.property_data = property_data
    self.widget = PropertyWidget(self)
    self.provider = None
    self.parameters = []

  def getWidget(self):
    """Returns the widget used for seeding this property."""
    return self.widget

  def getName(self):
    """Returns the name of the property."""
    return self.property_data.name

  def getDataProviders(self):
    """Returns a list of DataProviderData objects compatible with this property
    type.

    """
    return DataProviderData.getDataProvidersForProperty(self.property_data.type)

  def clearDataProvider(self):
    """Clears the data provider for this property. """

    if (self.provider and
        isinstance(self.provider.data_provider_data, ReferenceDataProviderData)):
      parameter = self.parameters[0]
      parameter.model.remove(check_parent=False)
    self.parameters = []
    self.provider = None
    self.widget.description.clear()
    self.widget.parameters.clear()

  def setDataProvider(self, provider):
    """Set a data provider to use for seeding this property.

    Args:
      provider: A DataProvider object describing the data provider to use
                for this property.

    """
    self.parameters = []
    self.provider = provider
    for parameter in provider.getParameters():
      self.parameters.append(parameter)

  def getConfigurationSheet(self):
    """Returns the configuration sheet for seeding this property."""
    configuration_sheet = {}
    if self.provider:
      configuration_sheet = self.provider.getConfigurationSheet()

    return configuration_sheet


class DataProvider(object):
  """Holds information about a specific data provider used for seeding a
  Property.
  """

  def __init__(self, data_provider_data, property):
    """Builds the DataProvider object.

    Args:
      data_provider_data: A DataProviderData object to use for meta information.
      property: The Property that this object is used to seed.

    """
    self.property = property
    self.data_provider_data = data_provider_data
    self.parameters = []

    if isinstance(self.data_provider_data, NormalDataProviderData):
      for parameter_data in self.data_provider_data.parameters.itervalues():
        self.parameters.append(NormalParameter(parameter_data, self))
    elif isinstance(self.data_provider_data, ReferenceDataProviderData):
      self.parameters.append(ReferenceParameter(self))
    else:
      assert false

  def getParameters(self):
    """Returns a list of Parameter objects that relate to this DataProvider."""
    return self.parameters

  def getConfigurationSheet(self):
    """Returns the configuration sheet for this data provider."""
    configuration_sheet = {}
    configuration_sheet['provider_name'] = self.data_provider_data.name

    parameters = {}
    for parameter in self.parameters:
      parameters.update(parameter.getConfigurationSheet())

    if parameters:
      configuration_sheet['parameters'] = parameters

    return configuration_sheet

class Parameter(object):
  """Holds information about a specific parameter of a data provider for a model property."""

  def __init__(self, data_provider):
    """Build a Parameter object using meta information from a ParameterData object.

    Args:
      data_provider: The DataProvider object that this Parameter relates to.

    """
    self.data_provider = data_provider

  def getConfigurationSheet(self):
    """Returns the configuration sheet for this parameter."""
    raise NotImplementedError

  def getWidget(self):
    """Returns the widget used for configuring this parameter."""
    raise NotImplementedError

class NormalParameter(Parameter):
  def __init__(self, parameter_data, data_provider):
    """Build a NormalParameter object using meta information from a ParameterData object.

    Args:
      parameter_data: A ParameterData object to use for meta information.
      data_provider: The DataProvider object that this Parameter relates to.

    """
    super(NormalParameter, self).__init__(data_provider)
    self.parameter_data = parameter_data
    self.widget = ParameterWidget(self)

  def getConfigurationSheet(self):
    configuration_sheet = {}
    data = self.widget.getParameterData()
    if data:
      configuration_sheet[self.parameter_data.name] = data
    return configuration_sheet

  def getWidget(self):
    return self.widget

class ReferenceParameter(Parameter):
  def __init__(self, data_provider):
    """Build a ReferenceParameter object for a ReferenceDataProvider.

    Args:
      data_provider: The ReferenceDataProvider object that this Parameter relates to.

    """
    super(ReferenceParameter, self).__init__(data_provider)
    if data_provider.property.property_data.type == "ReferenceProperty":
      single_model = True
    elif data_provider.property.property_data.type == "_ReverseReferenceProperty":
      single_model = False

    model_data = ModelData.getModel(
        data_provider.property.property_data.reference_class)
    assert(model_data)
    reverse_property = data_provider.property.property_data.reverse_property
    self.model = Model(model_data, parent_reference_parameter=self,
                       single_model=single_model, ignore_property=reverse_property)

    self.widget = ModelLinkWidget(self.model, "Click here to configure the referenced model")

  def getConfigurationSheet(self):
    return self.model.getConfigurationSheet()

  def getWidget(self):
    return self.widget


class ModelLinkWidget(Hyperlink):
  def __init__(self, model, *args, **kwargs):
    super(ModelLinkWidget, self).__init__(*args, asHTML=True, **kwargs)
    self.model = model
    self.setTargetHistoryToken("")

    class ClickListener(object):
      def onClick(self2):
        seeder.showModel(self.model)
    self.addClickListener(ClickListener())


class PropertyWidget(CaptionPanel):
  """Widget representing a property."""

  def __init__(self, property):
    """Creates a PropertyWidget.

    Args:
      property: The Property object associated with this widget.

    """
    self.property = property
    property_data = property.property_data

    title = "%s - %s" % (property_data.name, property_data.type)
    if property_data.required:
      title += "(*)"
    super(self, PropertyWidget).__init__(self, title)

    self.contents = VerticalPanel()

    self.data_provider_listbox = ListBox()
    self.data_provider_listbox.setVisibleItemCount(0)

    self.data_provider_listbox.addItem("None", "")
    for provider in property.getDataProviders():
      self.data_provider_listbox.addItem(provider.name, provider.name)

    self.data_provider_listbox.addChangeListener(self.onDataProviderChanged)
    self.contents.add(self.data_provider_listbox)

    self.description = SimplePanel()
    self.contents.add(self.description)

    self.parameters = VerticalPanel()
    self.contents.add(self.parameters)

    self.add(self.contents)

  def onDataProviderChanged(self, listbox):
    """Fired when the data provider for this property was changed by the user."""
    self.description.clear()
    self.parameters.clear()
    provider_name = listbox.getValue(listbox.getSelectedIndex())
    self.property.clearDataProvider()
    if provider_name:
      provider_data = DataProviderData.getDataProvider(provider_name)
      provider = DataProvider(provider_data, self.property)
      assert(provider)
      self.description.add(HTML(provider_data.description))

      self.property.setDataProvider(provider)
      for parameter in self.property.parameters:
        self.parameters.add(parameter.getWidget())


class ParameterWidget(HorizontalPanel):
  """Widget representing a data provider parameter."""

  def __init__(self, parameter):
    """Initializez a widget for configuring a parameter.

    Args:
      parameter: The Parameter object associated with this widget.

    """
    super(self, ParameterWidget).__init__(self)

    self.parameter = parameter
    self.add(HTML(parameter.parameter_data.verbose_name))
    self.text_box = TextBox()
    self.add(self.text_box)

  def getParameterData(self):
    return self.text_box.getText()


class ModelWidget(VerticalPanel):
  """Widget representing a model."""

  def __init__(self, model):
    """Initializez a widget for configuring a model.

    Args:
      model: The Model object associated with this widget.

    """
    super(self, ModelWidget).__init__(self)

    self.model = model
    model_data = model.model_data

    self.header = DockPanel()
    self.header.add(HTML("<h2>%s</h2>" % model_data.name), DockPanel.WEST)
    self.delete_button = Button("Delete", self.onDelete)
    self.header.add(self.delete_button, DockPanel.EAST)
    self.header.setCellHorizontalAlignment(self.delete_button, HasAlignment.ALIGN_RIGHT)
    self.add(self.header)

    model_parent = model.getParent()
    if model_parent:
      self.add(ModelLinkWidget(model_parent, PARENT_MODEL_LINK))

    if model_data.description:
      self.description_panel = CaptionPanel("Description")
      self.description_panel.add(HTML(model_data.description.replace("\n", "<br />")))
      self.add(self.description_panel)

    if not self.model.single_model:
      self.add(Label("Number of models:"))
      self.number_text_box = TextBox()
      self.add(self.number_text_box)

    for property in model.properties:
      self.add(property.getWidget())

  def onDelete(self, event):
    self.model.remove()
    seeder.onTreeItemSelected(None)


class DataSeeder(object):
  def __init__(self):
    self.initWidgets()

    self.proxy = DataSeederProxy()
    self.callJSONRPC(self.proxy.getData, [], self.onGotData)

  def getButtonsPanel(self):
    footer = HorizontalPanel()
    btn1 = Button('Get configuration sheet', self.getConfigurationSheet)
    footer.add(btn1)
    #footer.add(btn1, DockPanel.WEST)
    #footer.setCellWidth(btn1, "50%")
    btn2 = Button('Send configuration sheet', self.sendConfigurationSheet)
    footer.add(btn2)
    #footer.add(btn2, DockPanel.EAST)
    #footer.setCellWidth(btn2, "50%")
    return footer

  def initWidgets(self):
    self.root = DockPanel()
    self.root.setStyleName("root")

    self.title = HTML("<h1>Data Seeder</h1>")
    self.title.setStyleName("title_bar")
    self.root.add(self.title, DockPanel.NORTH)

    self.model_sidebar = DockPanel()
    self.model_sidebar.setStyleName("model_sidebar")
    self.model_sidebar_top = HorizontalPanel()
    self.model_listbox = ListBox()
    self.model_listbox.setVisibleItemCount(0)
    self.add_model_button = Button('Add model', self.onAddModelClicked)
    self.model_sidebar_top.add(self.model_listbox)
    self.model_sidebar_top.add(self.add_model_button)
    self.model_sidebar.add(self.model_sidebar_top, DockPanel.NORTH)

    self.button_header = self.getButtonsPanel()
    self.model_sidebar.add(self.button_header, DockPanel.NORTH)
    self.model_sidebar.setCellWidth(self.button_header, "100%")

    self.tree = Tree()
    self.tree.addTreeListener(self)
    self.tree_panel = CaptionPanel("Model Hierarchy")
    self.tree_panel.setStyleName("model_sidebar_tree")
    self.tree_panel.add(HTML(DEFAULT_SIDEBAR_CONTENTS))
    self.model_sidebar.add(self.tree_panel, DockPanel.CENTER)
    self.model_sidebar.setCellHeight(self.tree_panel, "100%")

    self.button_footer = self.getButtonsPanel()
    self.model_sidebar.add(self.button_footer, DockPanel.SOUTH)
    self.model_sidebar.setCellWidth(self.button_footer, "100%")

    self.root.add(self.model_sidebar, DockPanel.WEST)
    self.root.setCellWidth(self.model_sidebar, "30%")
    self.root.setCellHeight(self.model_sidebar, "100%")

    self.help_sidebar = SimplePanel()
    self.help_sidebar.add(HTML(HELP_CONTENTS))
    self.help_sidebar.setStyleName("contents")
    self.root.add(self.help_sidebar, DockPanel.EAST)
    self.root.setCellWidth(self.help_sidebar, "30%")
    self.root.setCellHeight(self.help_sidebar, "100%")

    self.contents = SimplePanel()
    self.contents.add(HTML(DEFAULT_CONTENTS))
    self.contents.setStyleName("contents")
    self.root.add(self.contents, DockPanel.CENTER)
    self.root.setCellWidth(self.contents, "40%")
    self.root.setCellHeight(self.contents, "100%")


    RootPanel().add(self.root)

  def callJSONRPC(self, method, params, callback, error_callback):
    """Initiate a JSON-RPC call.

    Args:
      method: The method to call. Must be a method of JSONProxy object.
      params: A list of parameters for the call.
      callback: A method to be called if the call succeeds.
      error_callback: A method to be called if the call fails.

    """
    class CallbackWrapper(object):

      def onRemoteResponse(self, response, request_info):
        callback(response, request_info)

      def onRemoteError(self, code, errobj, request_info):
        error_callback(code, errobj, request_info)

    wrapper = CallbackWrapper()
    params.append(wrapper)
    method(*params)

  def onAddModelClicked(self, event):
    if self.tree.getItemCount() == 0:
      self.tree_panel.clear()
      self.tree_panel.add(self.tree)
    model_name = self.model_listbox.getItemText(self.model_listbox.getSelectedIndex())
    model_data = ModelData.getModel(model_name)
    assert(model_data)
    if model_data:
      model = Model(model_data)
      self.showModel(model)

  def onTreeItemSelected(self, item):
    self.contents.clear()
    if item:
      model = item.getUserObject()
      self.showModel(model)

  def showModel(self, model):
    self.contents.clear()

    self.contents.add(model.widget)
    if seeder.tree.getSelectedItem() != model.tree_item:
      seeder.tree.setSelectedItem(model.tree_item, True)

  def onGotData(self, response, request_info):
    """Callback fired when the getData JSON-RPC call succeeds.

    Args:
      response: A dict containing meta information, as follows:
                    'models': A list of dicts containing model information
                    'providers': A list of dicts containing data provider
                                 information
                    'providers_mapping': A dict mapping property types to a
                                         list of compatible data provider
                                         names
      request_info: Information about the JSON-RPC request.

    """
    models = response.get('models')
    if models:
      for model in models:
        ModelData.addModel(model)

    for name in sorted(ModelData.getModelNames()):
      self.model_listbox.addItem(name)

    # Create the special reference data providers
    provider = ReferenceDataProviderData({
        "name": NEW_REFERENCE_PROVIDER_NAME,
        "description": NEW_REFERENCE_PROVIDER_DESCRIPTION})
    DataProviderData.addDataProvider(provider)
    DataProviderData.addProviderMapping("ReferenceProperty", provider.name)

    provider = ReferenceDataProviderData({
        "name": NEW_BACK_REFERENCE_PROVIDER_NAME,
        "description": NEW_BACK_REFERENCE_PROVIDER_DESCRIPTION})
    DataProviderData.addDataProvider(provider)
    DataProviderData.addProviderMapping("_ReverseReferenceProperty",
        provider.name)

    providers = response.get('providers')
    if providers:
      for provider in providers:
        DataProviderData.addDataProvider(NormalDataProviderData(provider))

    providers_mapping = response.get('providers_mapping')
    if providers_mapping:
      for property_type, provider_list in providers_mapping.iteritems():
        for provider in provider_list:
          DataProviderData.addProviderMapping(property_type, provider)

  def onSendConfigurationSheetSucceeded(self, response, request_info):
    """Callback fired when the seed JSON-RPC call succeeds."""
    Window.alert("Success!!!")

  def onSendConfigurationSheetError(self, code, errobj, request_info):
    """Callback fired when the seed JSON-RPC call fails."""
    Window.alert("Error: %r" % errobj)

  def sendConfigurationSheet(self):
    """Initiate a JSON-RPC call to start the seeding operation."""
    config_sheet = self.getConfigurationSheet()
    params = [config_sheet]
    self.callJSONRPC(self.proxy.seed, params,
        self.onSendConfigurationSheetSucceeded, self.onSendConfigurationSheetError)

  def getConfigurationSheet(self):
    """Returns the current state of the configuration sheet."""
    config_sheet = []
    for i in range(self.tree.getItemCount()):
      model = self.tree.getItem(i).getUserObject()
      model_configuration_sheet = model.getConfigurationSheet()
      if model_configuration_sheet:
        config_sheet.append(model_configuration_sheet)

    Window.alert(config_sheet)
    return config_sheet

seeder = DataSeeder()
